/*
 * Created on 2005-6-11
 *
 */
package bma.common.langutil.core;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings("rawtypes")
public class ToStringUtil {

	static public String fieldReflect(Object obj) {
		return fieldReflect(obj, true);
	}

	static public String fieldReflect(Object obj, boolean appendClassName) {
		if (obj == null)
			return "null";
		return fieldReflect(obj, obj.getClass(), appendClassName);
	}

	static public String fieldReflect(Object obj, Class cls) {
		if (cls == null && obj != null)
			cls = obj.getClass();
		return fieldReflect(obj, cls, true);
	}

	static public String fieldReflect(Object obj, Class cls,
			boolean appendClassName) {
		return fieldReflect(obj, cls, appendClassName, null);
	}

	static public String fieldReflect(Object obj, Class cls,
			boolean appendClassName, Set<Integer> sets) {
		StringBuffer buf = new StringBuffer(512);
		fieldReflect(buf, obj, cls, appendClassName, sets);
		return buf.toString();
	}

	static public void fieldReflect(StringBuffer buf, Object obj, Class cls,
			boolean appendClassName, Set<Integer> sets) {
		if (cls == null && obj != null)
			cls = obj.getClass();
		if (appendClassName)
			buf.append(ClassUtil.getShortName(cls)).append("[");
		if (!fieldReflect(buf, obj, cls, sets)) {
			buf.append(obj);
		}
		if (appendClassName)
			buf.append("]");
	}

	static public boolean fieldReflect(StringBuffer buf, Object obj, Class cls,
			Set<Integer> sets) {
		if (obj == null)
			return false;
		if (cls == null)
			cls = obj.getClass();
		try {
			boolean hasField = false;
			Field[] fs = cls.getDeclaredFields();
			for (Field f : fs) {
				if (Modifier.isFinal(f.getModifiers()))
					continue;
				hasField = true;
				boolean b = f.isAccessible();
				f.setAccessible(true);
				buf.append(f.getName());
				buf.append("=");
				Object v = f.get(obj);
				toString(buf, v, sets);
				buf.append(";");
				f.setAccessible(b);
			}
			Class p = cls.getSuperclass();
			if (p != null && p != Object.class) {
				return fieldReflect(buf, obj, p, sets) || hasField;
			}
			return hasField;
		} catch (Exception e) {
			throw ExceptionUtil.throwRuntime(e);
		}

	}

	public static String toString(Object obj) {
		return toString(obj, true);
	}

	public static String toString(Object obj, boolean deep) {
		return toString(obj, deep ? new HashSet<Integer>(100) : null);
	}

	public static String toString(Object obj, Set<Integer> cyclicSets) {
		StringBuffer buf = new StringBuffer(512);
		toString(buf, obj, cyclicSets);
		return buf.toString();
	}

	public static void toString(StringBuffer buf, Object obj,
			Set<Integer> cyclicSets) {
		if (obj == null) {
			buf.append("<null>");
			return;
		}

		try {
			if (obj.getClass().isArray()) {
				buf.append("[");
				for (int i = 0; i < Array.getLength(obj); i++) {
					if (i != 0)
						buf.append(",");
					toString(buf, Array.get(obj, i), cyclicSets);
				}
				buf.append("]");
				return;
			} else if (obj instanceof Collection) {
				Collection c = (Collection) obj;
				Iterator it = c.iterator();
				buf.append("[");
				while (it.hasNext()) {
					toString(buf, it.next(), cyclicSets);
					if (it.hasNext())
						buf.append(",");
				}
				buf.append("]");
				return;
			} else if (obj instanceof Map) {
				Map c = (Map) obj;
				Iterator it = c.keySet().iterator();
				buf.append("[");
				while (it.hasNext()) {
					Object key = it.next();
					buf.append(key);
					buf.append("=");
					toString(buf, c.get(key), cyclicSets);
					if (it.hasNext())
						buf.append(",");
				}
				buf.append("]");
				return;
			} else {
				int v = System.identityHashCode(obj);
				if (cyclicSets == null || !cyclicSets.add(v)) {
					buf.append(obj.toString());
					return;
				}
				Class cls = obj.getClass();
				String code = cls.getName() + "@" + Integer.toHexString(v);
				String s = obj.toString();
				if (!code.equals(s)) {
					buf.append(s);
				} else {
					fieldReflect(buf, obj, cls, true, cyclicSets);
				}
				return;
			}
		} catch (Exception e) {
			throw ExceptionUtil.throwRuntime(e);
		}
	}
}
